#include <stdio.h>
#include <string.h>

#include "scene_parser.h"
#include "camera.h" 
#include "light.h"
#include "material.h"
#include "object3d.h"
#include "group.h" 
#include "sphere.h"
#include "plane.h"
#include "triangle.h"
//#include "box.h"
#include "transform.h"
#include "boundingSphere.h"

#include "cylinder.h"
#include "cone.h"
#include "csgobject.h"

#define DegreesToRadians(x) ((M_PI * x) / 180.0f)

// ====================================================================
// ====================================================================
// CONSTRUCTOR & DESTRUCTOR

SceneParser::SceneParser(const char* filename) {

  // initialize some reasonable default values
  group = NULL;
  camera = NULL;
  background_color = d_Vec3f(0.5,0.5,0.5);
  ambient_light = d_Vec3f(0,0,0);
  num_lights = 0;
  lights = NULL;
  num_materials = 0;
  materials = NULL;
  current_material = NULL;

  // parse the file
  assert(filename != NULL);
  const char *ext = &filename[strlen(filename)-4];
  assert(!strcmp(ext,".txt"));
  file = fopen(filename,"r");
  assert (file != NULL);
  parseFile();
  fclose(file); 
  file = NULL;

  // if no lights are specified, set ambient light to white
  // (do solid color ray casting)
  if (num_lights == 0) {
    printf ("WARNING:  No lights specified\n");
    ambient_light = d_Vec3f(1,1,1);
  }
}

SceneParser::~SceneParser() {
  if (group != NULL) 
    delete group;
  if (camera != NULL) 
    delete camera;
  int i;
  for (i = 0; i < num_materials; i++) {
    delete materials[i]; }
  delete [] materials;
  for (i = 0; i < num_lights; i++) {
    delete lights[i]; }
  delete [] lights;
}

// ====================================================================
// ====================================================================

void SceneParser::parseFile() {
  //
  // at the top level, the scene can have a camera, 
  // background color and a group of objects
  // (we add lights and other things in future assignments)
  //
  char token[MAX_PARSER_TOKEN_LENGTH];    
  while (getToken(token)) { 
    if (!strcmp(token, "OrthographicCamera")) {
      parseOrthographicCamera();
    } else if (!strcmp(token, "PerspectiveCamera")) {
      parsePerspectiveCamera();
    } else if (!strcmp(token, "Background")) {
      parseBackground();
    } else if (!strcmp(token, "Lights")) {
      parseLights();
    } else if (!strcmp(token, "Materials")) {
      parseMaterials();
    } else if (!strcmp(token, "Group")) {
      group = parseGroup();
    } else {
      printf ("Unknown token in parseFile: '%s'\n", token);
      exit(0);
    }
  }
}

// ====================================================================
// ====================================================================

void SceneParser::parseOrthographicCamera() {
  char token[MAX_PARSER_TOKEN_LENGTH];
  // read in the camera parameters
  getToken(token); assert (!strcmp(token, "{"));
  getToken(token); assert (!strcmp(token, "center"));
  d_Vec3f center = readVec3f();
  getToken(token); assert (!strcmp(token, "direction"));
  d_Vec3f direction = readVec3f();
  getToken(token); assert (!strcmp(token, "up"));
  d_Vec3f up = readVec3f();
  getToken(token); assert (!strcmp(token, "size"));
  float size = readFloat();
  getToken(token); assert (!strcmp(token, "}"));
  camera = new OrthographicCamera(center,direction,up,size);
}


void SceneParser::parsePerspectiveCamera() {
  char token[MAX_PARSER_TOKEN_LENGTH];
  // read in the camera parameters
  getToken(token); assert (!strcmp(token, "{"));
  getToken(token); assert (!strcmp(token, "center"));
  d_Vec3f center = readVec3f();
  getToken(token); assert (!strcmp(token, "direction"));
  d_Vec3f direction = readVec3f();
  getToken(token); assert (!strcmp(token, "up"));
  d_Vec3f up = readVec3f();
  getToken(token); assert (!strcmp(token, "angle"));
  float angle_degrees = readFloat();
  float angle_radians = DegreesToRadians(angle_degrees);
  getToken(token); assert (!strcmp(token, "}"));
  camera = new PerspectiveCamera(center,direction,up,angle_radians);
}

void SceneParser::parseBackground() {
  char token[MAX_PARSER_TOKEN_LENGTH];
  // read in the background color
  getToken(token); assert (!strcmp(token, "{"));  
  while (1) {
    getToken(token); 
    if (!strcmp(token, "}")) { 
      break;  
    } else if (!strcmp(token, "color")) {
      background_color = readVec3f();
    } else if (!strcmp(token, "ambientLight")) {
      ambient_light = readVec3f();
    } else {
      printf ("Unknown token in parseBackground: '%s'\n", token);
      assert(0);
    }
  }
}

// ====================================================================
// ====================================================================

void SceneParser::parseLights() {
  char token[MAX_PARSER_TOKEN_LENGTH];
  getToken(token); assert (!strcmp(token, "{"));
  // read in the number of objects
  getToken(token); assert (!strcmp(token, "numLights"));
  num_lights = readInt();
  lights = new Light*[num_lights];
  // read in the objects
  int count = 0;
  while (num_lights > count) {
    getToken(token); 
    if (!strcmp(token, "DirectionalLight")) {
      lights[count] = parseDirectionalLight();
    } else if (!strcmp(token, "PointLight")) {
      lights[count] = parsePointLight();
    } else if (!strcmp(token, "SpotLight")) {
      lights[count] = parseSpotLight();
    } else if (!strcmp(token, "UniformLight")) {
      lights[count] = parseUniformLight();
    } else if (!strcmp(token, "AreaLight")) {
      lights[count] = parseAreaLight();
    } else {
      printf ("Unknown token in parseLight: '%s'\n", token); 
      exit(0);  
    }   
    count++;
  }
  getToken(token); assert (!strcmp(token, "}"));
}


Light* SceneParser::parseDirectionalLight() {
  char token[MAX_PARSER_TOKEN_LENGTH];
  getToken(token); assert (!strcmp(token, "{"));
  getToken(token); assert (!strcmp(token, "direction"));
  d_Vec3f direction = readVec3f();
  getToken(token); assert (!strcmp(token, "color"));
  d_Vec3f color = readVec3f();
  getToken(token); assert (!strcmp(token, "}"));
  return new DirectionalLight(direction,color);
}


Light* SceneParser::parsePointLight() {
  char token[MAX_PARSER_TOKEN_LENGTH];
  getToken(token); assert (!strcmp(token, "{"));
  getToken(token); assert (!strcmp(token, "position"));
  d_Vec3f position = readVec3f();
  getToken(token); assert (!strcmp(token, "color"));
  d_Vec3f color = readVec3f();
  float att[3] = { 1, 0, 0 };
  getToken(token); 
  if (!strcmp(token,"attenuation")) {
    att[0] = readFloat();
    att[1] = readFloat();
    att[2] = readFloat();
    getToken(token); 
  } 
  assert (!strcmp(token, "}"));
  return new PointLight(position,color,att[0], att[1], att[2]);
}

Light* SceneParser::parseSpotLight() {
  char token[MAX_PARSER_TOKEN_LENGTH];
  getToken(token); assert (!strcmp(token, "{"));
  getToken(token); assert (!strcmp(token, "position"));
  d_Vec3f position = readVec3f();
  getToken(token); assert (!strcmp(token, "color"));
  d_Vec3f color = readVec3f();
  getToken(token); assert (!strcmp(token, "radius"));
  float  radius = DegreesToRadians(readFloat());
  getToken(token); assert (!strcmp(token, "falloff"));
  float  falloff = DegreesToRadians(readFloat());
  getToken(token); assert (!strcmp(token, "direction"));
  d_Vec3f direction = readVec3f();
  float att[3] = { 1, 0, 0 };
  getToken(token); 
  if (!strcmp(token,"attenuation")) {
    att[0] = readFloat();
    att[1] = readFloat();
    att[2] = readFloat();
    getToken(token); 
  } 
  assert (!strcmp(token, "}"));
  return new SpotLight(position, direction, color, radius, falloff);
}

Light* SceneParser::parseUniformLight() {
  char token[MAX_PARSER_TOKEN_LENGTH];
  getToken(token); assert (!strcmp(token, "{"));
  getToken(token); assert (!strcmp(token, "color"));
  d_Vec3f color = readVec3f();
  getToken(token); assert (!strcmp(token, "}"));
  return new UniformLight(color);
}

Light* SceneParser::parseAreaLight() {
  char token[MAX_PARSER_TOKEN_LENGTH];
  getToken(token); assert (!strcmp(token, "{"));
  getToken(token); assert (!strcmp(token, "color"));
  d_Vec3f color = readVec3f();
  getToken(token); assert (!strcmp(token, "position"));
  d_Vec3f position = readVec3f();
  getToken(token); assert (!strcmp(token, "width"));
  float w = readFloat();
  getToken(token); assert (!strcmp(token, "height"));
  float h = readFloat();
  getToken(token); assert (!strcmp(token, "attenuation"));
  d_Vec3f at = readVec3f();
  getToken(token); assert (!strcmp(token, "}"));
  return new AreaLight(position,color,w,h,at);
}

// ====================================================================
// ====================================================================

void SceneParser::parseMaterials() {
  char token[MAX_PARSER_TOKEN_LENGTH];
  getToken(token); assert (!strcmp(token, "{"));
  // read in the number of objects
  getToken(token); assert (!strcmp(token, "numMaterials"));
  num_materials = readInt();
  materials = new Material*[num_materials];
  // read in the objects
  int count = 0;
  while (num_materials > count) {
    getToken(token); 
    if (!strcmp(token, "Material") ||
        !strcmp(token, "PhongMaterial")) {
      materials[count] = parsePhongMaterial();
    } else if (!strcmp(token, "CookTorranceMaterial")) {
      materials[count] = parseCookTorranceMaterial();
    } else if (!strcmp(token, "Checkerboard")) {
      materials[count] = parseCheckerboard(count);
    } else if (!strcmp(token, "Noise")) {
      materials[count] = parseNoise(count);
    } else if (!strcmp(token, "Marble")) {
      materials[count] = parseMarble(count);
    } else if (!strcmp(token, "Wood")) {
      materials[count] = parseWood(count);
    } else {
      printf ("Unknown token in parseMaterial: '%s'\n", token); 
      exit(0);
    }
    count++;
  }
  getToken(token); assert (!strcmp(token, "}"));
}  


Material* SceneParser::parsePhongMaterial() {
  char token[MAX_PARSER_TOKEN_LENGTH];
  d_Vec3f diffuseColor(1,1,1);
  d_Vec3f specularColor(0,0,0);
  float exponent = 1;
  d_Vec3f reflectiveColor(0,0,0);
  d_Vec3f transparentColor(0,0,0);
  float indexOfRefraction = 1;
  d_Mat4f *matrix = NULL;
  char* texture = NULL;
  char* bumpmap = NULL;
  int mipmap = 0;
  int linearInterp = 0;
  getToken(token); assert (!strcmp(token, "{"));
  while (1) {
    getToken(token); 
	if (!strcmp(token, "Transform")) {
		matrix = new d_Mat4f();
		matrix->diagonal(1);
		getToken(token); assert (!strcmp(token, "{"));
		parseMatrixHelper(*matrix,token);
		assert (!strcmp(token, "}"));
		getToken(token);
	}
    if (!strcmp(token, "diffuseColor")) {
      diffuseColor = readVec3f();
    } else if (!strcmp(token, "specularColor")) {
      specularColor = readVec3f();
    } else if  (!strcmp(token, "exponent")) {
      exponent = readFloat();
    } else if (!strcmp(token, "reflectiveColor")) {
      reflectiveColor = readVec3f();
    } else if (!strcmp(token, "transparentColor")) {
      transparentColor = readVec3f();
	} else if (!strcmp(token, "indexOfRefraction")) {
      indexOfRefraction = readFloat();
    } else if (!strcmp(token, "texture")) {
      getToken(token); 
      texture = new char[strlen(token)+2];
      strcpy(texture,token);
    } else if (!strcmp(token, "bumpmap")) {
      getToken(token); 
      bumpmap = new char[strlen(token)+2];
      strcpy(bumpmap,token);
    } else if (!strcmp(token, "mipmap")) {
	  mipmap = 1;
    } else if (!strcmp(token, "linearInterpolation")) {
      linearInterp = 1;
      printf ("LINEAR\n");
    } else {
      assert (!strcmp(token, "}"));
      break;
    }
  }
  Material *answer = new PhongMaterial
    (diffuseColor,specularColor,exponent,
     reflectiveColor,transparentColor,indexOfRefraction,matrix, texture, bumpmap);
  //if (mipmap) answer->setDoMipMap();
  //if (linearInterp) answer->setDoLinearInterp();
  return answer;
}

Material* SceneParser::parseCookTorranceMaterial() {
  char token[MAX_PARSER_TOKEN_LENGTH];
  d_Vec3f diffuseColor(1,1,1);
  d_Vec3f specularColor(0,0,0);
  float alpha = 1;
  float roughness = 1;
  d_Vec3f reflectiveColor(0,0,0);
  d_Vec3f transparentColor(0,0,0);
  d_Mat4f *matrix = NULL;
  int linearInterp = 0;
  getToken(token); assert (!strcmp(token, "{"));
  while (1) {
    getToken(token); 
	if (!strcmp(token, "Transform")) {
		matrix = new d_Mat4f();
		matrix->diagonal(1);
		getToken(token); assert (!strcmp(token, "{"));
		parseMatrixHelper(*matrix,token);
		assert (!strcmp(token, "}"));
		getToken(token);
	}
    if (!strcmp(token, "diffuseColor")) {
      diffuseColor = readVec3f();
    } else if (!strcmp(token, "specularColor")) {
      specularColor = readVec3f();
    } else if  (!strcmp(token, "alpha")) {
      alpha = readFloat();
    } else if  (!strcmp(token, "roughness")) {
      roughness = readFloat();
    } else if (!strcmp(token, "reflectiveColor")) {
      reflectiveColor = readVec3f();
    } else if (!strcmp(token, "transparentColor")) {
      transparentColor = readVec3f();
	} else {
      assert (!strcmp(token, "}"));
      break;
    }
  }
  Material *answer = new CookTorranceMaterial
    (diffuseColor,specularColor,alpha,roughness,
     reflectiveColor,transparentColor,matrix);
  //if (mipmap) answer->setDoMipMap();
  //if (linearInterp) answer->setDoLinearInterp();
  return answer;
}


Material *SceneParser::parseCheckerboard(int count) {
  char token[MAX_PARSER_TOKEN_LENGTH];
  getToken(token); assert (!strcmp(token, "{"));
  d_Mat4f *matrix = NULL; 
  getToken(token); 
  if (!strcmp(token, "Transform")) {
    matrix = new d_Mat4f();
    matrix->diagonal(1);
    getToken(token); assert (!strcmp(token, "{"));
    parseMatrixHelper(*matrix,token);
    assert (!strcmp(token, "}"));
    getToken(token); 
  }
  assert (!strcmp(token, "materialIndex"));
  int m1 = readInt();
  assert (m1 >= 0 && m1 < count);
  getToken(token); assert (!strcmp(token, "materialIndex"));
  int m2 = readInt();
  assert (m2 >= 0 && m2 < count);
  getToken(token); assert (!strcmp(token, "}"));
  return new Checkerboard(matrix, materials[m1],materials[m2]);
}


Material *SceneParser::parseNoise(int count) {
  char token[MAX_PARSER_TOKEN_LENGTH];
  getToken(token); assert (!strcmp(token, "{"));
  d_Mat4f *matrix = NULL; 
  getToken(token); 
  if (!strcmp(token, "Transform")) {
    matrix = new d_Mat4f();
    matrix->diagonal(1);
    getToken(token); assert (!strcmp(token, "{"));
    parseMatrixHelper(*matrix,token);
    assert (!strcmp(token, "}"));
    getToken(token); 
  }
  assert (!strcmp(token, "materialIndex"));
  int m1 = readInt();
  assert (m1 >= 0 && m1 < count);
  getToken(token); assert (!strcmp(token, "materialIndex"));
  int m2 = readInt();
  assert (m2 >= 0 && m2 < count);
  getToken(token); assert (!strcmp(token, "octaves"));
  int octaves = readInt();
  getToken(token); assert (!strcmp(token, "}"));
  return new Noise(matrix,materials[m1],materials[m2],octaves);
}


Material *SceneParser::parseMarble(int count) {
  char token[MAX_PARSER_TOKEN_LENGTH];
  getToken(token); assert (!strcmp(token, "{"));
  d_Mat4f *matrix = NULL; 
  getToken(token); 
  if (!strcmp(token, "Transform")) {
    matrix = new d_Mat4f();
    matrix->diagonal(1);
    getToken(token); assert (!strcmp(token, "{"));
    parseMatrixHelper(*matrix,token);
    assert (!strcmp(token, "}"));
    getToken(token); 
  }
  assert (!strcmp(token, "materialIndex"));
  int m1 = readInt();
  assert (m1 >= 0 && m1 < count);
  getToken(token); assert (!strcmp(token, "materialIndex"));
  int m2 = readInt();
  assert (m2 >= 0 && m2 < count);
  getToken(token); assert (!strcmp(token, "octaves"));
  int octaves = readInt();
  getToken(token); assert (!strcmp(token, "frequency"));
  float frequency = readFloat();
  getToken(token); assert (!strcmp(token, "amplitude"));
  float amplitude = readFloat();
  getToken(token); assert (!strcmp(token, "}"));
  return new Marble(matrix,materials[m1],materials[m2],octaves,frequency,amplitude);
}


Material *SceneParser::parseWood(int count) {
  char token[MAX_PARSER_TOKEN_LENGTH];
  getToken(token); assert (!strcmp(token, "{"));
  d_Mat4f *matrix = NULL; 
  getToken(token); 
  if (!strcmp(token, "Transform")) {
    matrix = new d_Mat4f();
    matrix->diagonal(1);
    getToken(token); assert (!strcmp(token, "{"));
    parseMatrixHelper(*matrix,token);
    assert (!strcmp(token, "}"));
    getToken(token); 
  }
  assert (!strcmp(token, "materialIndex"));
  int m1 = readInt();
  assert (m1 >= 0 && m1 < count);
  getToken(token); assert (!strcmp(token, "materialIndex"));
  int m2 = readInt();
  assert (m2 >= 0 && m2 < count);
  getToken(token); assert (!strcmp(token, "octaves"));
  int octaves = readInt();
  getToken(token); assert (!strcmp(token, "frequency"));
  float frequency = readFloat();
  getToken(token); assert (!strcmp(token, "amplitude"));
  float amplitude = readFloat();
  getToken(token); assert (!strcmp(token, "}"));
  return NULL;//new Wood(matrix,materials[m1],materials[m2],octaves,frequency,amplitude);
}

// ====================================================================
// ====================================================================

Object3D* SceneParser::parseObject(char token[MAX_PARSER_TOKEN_LENGTH]) {
  Object3D *answer = NULL;
  if (!strcmp(token, "Group")) {      
    answer = (Object3D*)parseGroup();
  } else if (!strcmp(token, "Sphere")) {      
    answer = (Object3D*)parseSphere();
  } else if (!strcmp(token, "Plane")) {      
    answer = (Object3D*)parsePlane();
  } else if (!strcmp(token, "Triangle")) {      
    answer = (Object3D*)parseTriangle();
  } else if (!strcmp(token, "Box")) {      
    answer = (Object3D*)parseBox();
  } else if (!strcmp(token, "TriangleMesh")) {      
    answer = (Object3D*)parseTriangleMesh();
  } else if (!strcmp(token, "Transform")) {      
    answer = (Object3D*)parseTransform();
  } else if (!strcmp(token, "BoundingSphere")) {
    answer = (Object3D*)parseBoundingSphere();
  } else if (!strcmp(token, "CSGObject")) {
    answer = (Object3D*)parseCSGObject();
  } else {
    printf ("Unknown token in parseObject: '%s'\n", token);
    exit(0);
  }
  return answer;
}

// ====================================================================
// ====================================================================

Group* SceneParser::parseGroup() {
  //
  // each group starts with an integer that specifies
  // the number of objects in the group
  //
  // the material index sets the material of all objects which follow,
  // until the next material index (scoping for the materials is very
  // simple, and essentially ignores any tree hierarchy)
  //
  char token[MAX_PARSER_TOKEN_LENGTH];
  getToken(token); assert (!strcmp(token, "{"));

  // read in the number of objects
  getToken(token); assert (!strcmp(token, "numObjects"));
  int num_objects = readInt();

  Group *answer = new Group(num_objects);

  // read in the objects
  int count = 0;
  while (num_objects > count) {
    getToken(token); 
    if (!strcmp(token, "MaterialIndex")) {
      // change the current material
      int index = readInt();
      assert (index >= 0 && index <= getNumMaterials());
      current_material = getMaterial(index);
    } else {
      Object3D *object = parseObject(token);
      assert (object != NULL);
      answer->addObject(count,object);
      count++;
    }
  }
  getToken(token); assert (!strcmp(token, "}"));
  
  // return the group
  return answer;
}

// ====================================================================
// ====================================================================

Sphere* SceneParser::parseSphere() {
  char token[MAX_PARSER_TOKEN_LENGTH];
  getToken(token); assert (!strcmp(token, "{"));
  getToken(token); assert (!strcmp(token, "center"));
  d_Vec3f center = readVec3f();
  getToken(token); assert (!strcmp(token, "radius"));
  float radius = readFloat();
  getToken(token); assert (!strcmp(token, "}"));
  assert (current_material != NULL);
  return new Sphere(center,radius,current_material);
}


Plane* SceneParser::parsePlane() {
  char token[MAX_PARSER_TOKEN_LENGTH];
  getToken(token); assert (!strcmp(token, "{"));
  getToken(token); assert (!strcmp(token, "normal"));
  d_Vec3f normal = readVec3f();
  getToken(token); assert (!strcmp(token, "offset"));
  float offset = readFloat();
  getToken(token); assert (!strcmp(token, "}"));
  assert (current_material != NULL);
  return new Plane(normal,offset,current_material);
}


Triangle* SceneParser::parseTriangle() {
  char token[MAX_PARSER_TOKEN_LENGTH];
  getToken(token); assert (!strcmp(token, "{"));
  d_Vec2f t0 = d_Vec2f(0,0);
  d_Vec2f t1 = d_Vec2f(0,0);
  d_Vec2f t2 = d_Vec2f(0,0);
  getToken(token); 
  if (!strcmp(token, "textCoord0")) { t0 = readVec2f(); getToken(token); }
  assert (!strcmp(token, "vertex0"));
  d_Vec3f v0 = readVec3f();
  getToken(token); 
  if (!strcmp(token, "textCoord1")) { t1 = readVec2f(); getToken(token); }
  assert (!strcmp(token, "vertex1"));
  d_Vec3f v1 = readVec3f();
  getToken(token); 
  if (!strcmp(token, "textCoord2")) { t2 = readVec2f(); getToken(token); }
  assert (!strcmp(token, "vertex2"));
  d_Vec3f v2 = readVec3f();
  getToken(token); assert (!strcmp(token, "}"));
  assert (current_material != NULL);
  return new Triangle(v0,v1,v2,current_material);
}


Box* SceneParser::parseBox() {
  char token[MAX_PARSER_TOKEN_LENGTH];
  getToken(token); assert (!strcmp(token, "{"));
  getToken(token); assert (!strcmp(token, "min"));
  d_Vec3f min = readVec3f();
  getToken(token); assert (!strcmp(token, "max"));
  d_Vec3f max = readVec3f();
  getToken(token); assert (!strcmp(token, "}"));
  assert (current_material != NULL);
  return NULL;//new Box(min,max,current_material);
}


Group* SceneParser::parseTriangleMesh() {
  char token[MAX_PARSER_TOKEN_LENGTH];
  char filename[MAX_PARSER_TOKEN_LENGTH];
  bool isTextured = false;
  char texfile[MAX_PARSER_TOKEN_LENGTH];
  // get the filename
  getToken(token); assert (!strcmp(token, "{"));
  getToken(token); assert (!strcmp(token, "obj_file"));
  getToken(filename); 
  getToken(token);
  if(!strcmp(token, "tex_file"))
  {
	  getToken(texfile);
	  isTextured = true;
	  getToken(token);
  }
  assert (!strcmp(token, "}"));
  const char *ext = &filename[strlen(filename)-4];
  assert(!strcmp(ext,".obj"));
  // read it once, get counts
  FILE *file = fopen(filename,"r");
  assert (file != NULL);
  int vcount = 0; int fcount = 0; int tcount = 0;
  while (1) {
    int c = fgetc(file);
    if (c == EOF) { break;
    } else if (c == 'v') { 
      assert(fcount == 0); float v0,v1,v2;
      fscanf (file,"%f %f %f",&v0,&v1,&v2);
      vcount++; 
    } else if (c == 't') {
		//if(!isTextured)
		//	isTextured=true;
		float t0, t1;
		fscanf (file, "%f %f", &t0, &t1);
		tcount++;
	} else if (c == 'f') {
      int f0,f1,f2;
	  int tc0, tc1, tc2;
	  if(isTextured)
		  fscanf (file,"%d/%d %d/%d %d/%d",&f0, &tc0,&f1,&tc1,&f2,&tc2);
	  else
		  fscanf (file,"%d %d %d",&f0,&f1,&f2);
      fcount++; 
    }
  }
  fclose(file);

  // make arrays
  d_Vec3f *verts = new d_Vec3f[vcount];
  Group *answer = new Group(fcount);
  d_Vec2f *texcoords = new d_Vec2f[tcount];
  if(tcount > 0)
	  isTextured=true;

  // read it again, save it
  file = fopen(filename,"r");
  assert (file != NULL);
  int new_vcount = 0; int new_fcount = 0; int new_tcount = 0;
  while (1) {
    int c = fgetc(file);
    if (c == EOF) { break;
    } else if (c == 'v') { 
      assert(new_fcount == 0); float v0,v1,v2;
      fscanf (file,"%f %f %f",&v0,&v1,&v2);
      verts[new_vcount] = d_Vec3f(v0,v1,v2);
      new_vcount++; 
    } else if (c == 't') {
		assert(new_fcount ==0);
		float t0, t1;
		fscanf (file, "%f %f", &t0, &t1);
		texcoords[new_tcount] = d_Vec2f(t0,t1);
		new_tcount++;
	} else if (c == 'f') {
      assert (vcount == new_vcount);
      int f0,f1,f2;
	  int tc0, tc1, tc2;
	  if(isTextured)
		  fscanf (file,"%d/%d %d/%d %d/%d",&f0, &tc0,&f1,&tc1,&f2,&tc2);
	  else
		  fscanf (file,"%d %d %d",&f0,&f1,&f2);
      // indexed starting at 1...
      assert (f0 > 0 && f0 <= vcount);
      assert (f1 > 0 && f1 <= vcount);
      assert (f2 > 0 && f2 <= vcount);
      assert (current_material != NULL);
	  Triangle *t;
	  if(isTextured)
		  t = new Triangle(verts[f0-1],verts[f1-1],verts[f2-1],current_material, texcoords[tc0], texcoords[tc1], texcoords[tc2], texfile);
	  else
		  t = new Triangle(verts[f0-1],verts[f1-1],verts[f2-1],current_material);

      answer->addObject(new_fcount,t);
      new_fcount++; 
    } // otherwise, must be whitespace
  }
  delete [] verts;
  assert (fcount == new_fcount);
  assert (vcount == new_vcount);
  assert (tcount == new_tcount);
  fclose(file);
  return answer;
}

Transform* SceneParser::parseTransform() {
  char token[MAX_PARSER_TOKEN_LENGTH];
  d_Mat4f matrix; matrix.diagonal(1);
  // opening brace
  getToken(token); assert (!strcmp(token, "{"));
  // the matrix
  parseMatrixHelper(matrix,token);
  // the Object3D
  Object3D *object = parseObject(token);
  assert(object != NULL);
  // closing brace
  getToken(token); assert (!strcmp(token, "}"));
  return new Transform(matrix, object);
}

BoundingSphere *SceneParser::parseBoundingSphere() {
  char token[MAX_PARSER_TOKEN_LENGTH];
  // opening brace
  getToken(token); assert (!strcmp(token, "{"));

  getToken(token); assert (!strcmp(token, "center"));
  d_Vec3f center = readVec3f();
  getToken(token); assert (!strcmp(token, "radius"));

  float radius = readFloat();
  // closing brace
  // the Object3D
  getToken(token);
  Object3D *object = parseObject(token);
  assert(object != NULL);

  getToken(token); 
  assert (!strcmp(token, "}"));

  assert (current_material != NULL);
  return new BoundingSphere(center,radius,object,current_material);
}

void SceneParser::parseMatrixHelper(d_Mat4f &matrix, char token[MAX_PARSER_TOKEN_LENGTH]) {
  // read in transformations: 
  // apply to the LEFT side of the current matrix (so the first
  // transform in the list is the last applied to the object)
  while (1) {
    getToken(token);
    if (!strcmp(token,"Scale")) {
      matrix *= HScale4f(readVec3f());
    } else if (!strcmp(token,"UniformScale")) {
      float s = readFloat();
      matrix *= HScale4f(d_Vec3f(s,s,s));
    } else if (!strcmp(token,"Translate")) {
      matrix *=  HTrans4f(readVec3f());
    } else if (!strcmp(token,"XRotate")) {
      matrix *= HXRot4f(DegreesToRadians(readFloat()));
    } else if (!strcmp(token,"YRotate")) {
      matrix *= HYRot4f(DegreesToRadians(readFloat()));
    } else if (!strcmp(token,"ZRotate")) {
      matrix *= HZRot4f(DegreesToRadians(readFloat()));
    } else if (!strcmp(token,"Rotate")) {
      getToken(token); assert (!strcmp(token, "{"));
      d_Vec3f axis = readVec3f();
      float degrees = readFloat();
      matrix *= HRot4f(axis,DegreesToRadians(degrees));
      getToken(token); assert (!strcmp(token, "}"));
    } else if (!strcmp(token,"Matrix")) {
      d_Mat4f matrix2; matrix2.diagonal(1);
      getToken(token); assert (!strcmp(token, "{"));
      for (int j = 0; j < 4; j++) {
	for (int i = 0; i < 4; i++) {
	  float v = readFloat();
	  matrix2(i,j) = v; 
	} 
      }
      getToken(token); assert (!strcmp(token, "}"));
      matrix = matrix2 * matrix;
    } else {
      // otherwise this must be the thing to transform
      break;
    }
  }
}

// ====================================================================
// ====================================================================

int SceneParser::getToken(char token[MAX_PARSER_TOKEN_LENGTH]) {
  // for simplicity, tokens must be separated by whitespace
  assert (file != NULL);
  int success = fscanf(file,"%s ",token);
  if (success == EOF) {
    token[0] = '\0';
    return 0;
  }
  return 1;
}


d_Vec3f SceneParser::readVec3f() {
  float x,y,z;
  int count = fscanf(file,"%f %f %f",&x,&y,&z);
  if (count != 3) {
    printf ("Error trying to read 3 floats to make a d_Vec3f\n");
    assert (0);
  }
  return d_Vec3f(x,y,z);
}


d_Vec2f SceneParser::readVec2f() {
  float u,v;
  int count = fscanf(file,"%f %f",&u,&v);
  if (count != 2) {
    printf ("Error trying to read 2 floats to make a d_Vec2f\n");
    assert (0);
  }
  return d_Vec2f(u,v);
}


float SceneParser::readFloat() {
  float answer;
  int count = fscanf(file,"%f",&answer);
  if (count != 1) {
    printf ("Error trying to read 1 float\n");
    assert (0);
  }
  return answer;
}


int SceneParser::readInt() {
  int answer;
  int count = fscanf(file,"%d",&answer);
  if (count != 1) {
    printf ("Error trying to read 1 int\n");
    assert (0);
  }
  return answer;
}

// ====================================================================
// ====================================================================


////////////////////////

Cylinder* SceneParser::parseCylinder() {
  char token[MAX_PARSER_TOKEN_LENGTH];
  getToken(token); assert (!strcmp(token, "{"));
  getToken(token); assert (!strcmp(token, "radius"));
  float radius = readFloat();
  getToken(token); assert (!strcmp(token, "height"));
  float height = readFloat();
  getToken(token); assert (!strcmp(token, "}"));
  assert (current_material != NULL);
  return new Cylinder(radius, height, current_material);
}

Cone* SceneParser::parseCone() {
  char token[MAX_PARSER_TOKEN_LENGTH];
  getToken(token); assert (!strcmp(token, "{"));
  getToken(token); assert (!strcmp(token, "radius"));
  float radius = readFloat();
  getToken(token); assert (!strcmp(token, "height"));
  float height = readFloat();
  getToken(token); assert (!strcmp(token, "}"));
  assert (current_material != NULL);
  return new Cone(radius, height, current_material);
}


CSGObject* SceneParser::parseCSGObject() {
  char token[MAX_PARSER_TOKEN_LENGTH];
  char operation;
  // opening brace
  getToken(token); assert (!strcmp(token, "{"));
  // the matrix
  getToken(token); assert (!strcmp(token, "operation"));
  // the Object3D
  int type;
  float tmp = readFloat();
  if(tmp ==1)
	  type = CSG_UNION;
  if(tmp ==0)
	  type = CSG_INTERSECTION;
  if(tmp ==-1)
	  type = CSG_DIFFERENCE;
  getToken(token); 
  if (!strcmp(token, "MaterialIndex")) {
      // change the current material
      int index = readInt();
      assert (index >= 0 && index <= getNumMaterials());
      current_material = getMaterial(index);
  }
  getToken(token);
  Object3D *obj1 = parseObject(token);
  getToken(token); 
  if (!strcmp(token, "MaterialIndex")) {
      // change the current material
      int index = readInt();
      assert (index >= 0 && index <= getNumMaterials());
      current_material = getMaterial(index);
  }
  getToken(token);
  Object3D *obj2 = parseObject(token);
  assert(obj1 != NULL);
  assert(obj2 != NULL);
  // closing brace
  getToken(token); assert (!strcmp(token, "}"));
  return new CSGObject(obj1, obj2, type);
}

///////////////////////
